home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / prog_d / aspell22.zip / RICHCK32.PAS < prev    next >
Pascal/Delphi Source File  |  1996-04-03  |  23KB  |  445 lines

  1. unit Richck32;
  2.  
  3. interface
  4.  
  5. { Revisions:
  6.     03/15/96 - Component created
  7. }
  8.  
  9. uses
  10.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  11.   StdCtrls, ComCtrls, SugDlg32;
  12.  
  13. const SpellCharSet : set of char =
  14.        ['A'..'Z','a'..'z',#138,#140,#159,#192..#214,
  15.         #216..#223,#240,#154,#156,#224..#239,#241..#246,#248..#255];
  16.  
  17. type SuggestionType = (stNoSuggest, stCloseMatch, stPhoneme);
  18.  
  19. type
  20.   TRichSpell = class(TComponent)
  21.   private
  22.     { Private declarations }
  23.     FSuggestType         : SuggestionType;  { Holds the default initial suggestion type }
  24.     FDictionaryMain      : string;          { Holds the name of the main dictionary file }
  25.     FDictionaryUser      : string;          { Holds the name of the user's custom dictionary file }
  26.     FSuggestMax          : byte;            { Holds the maximum number of suggestions to return }
  27.     UserDictID           : integer;         { Holds the ID number ofhte open user dictionary }
  28.     FLeaveDictionaryOpen : boolean;         { Should we leave the dictionary files open? }
  29.     FDictionaryOpen      : boolean;         { Is the dictionary open? }
  30.     FAvoidHighlight      : boolean;         { Should the dialog avoid the highlighted text? }
  31.   protected
  32.     { Protected declarations }
  33.     DictDataPtr: pointer;                  { Pointer to internal dictionary data }
  34.     SuggestDlg : TSugDialog;               { The dialog box for this component }
  35.     StartWord  : string;                   { Temporary place to store the word being tested }
  36.     IgnoreList : TStringList;              { List of words to ignore }
  37.     ReplaceList   : TStringList;           { Replacement word list }
  38.     AlternateList : TStringList;           { Replacement word alternate word list }
  39.     procedure BaseCheckRich(var TheEditor : TRichEdit;
  40.                             StartLine : longint; StartCol : integer;
  41.                             EndLine   : longint; EndCol   : integer);
  42.     procedure SetDialogPosition(var TheEditor : TRichEdit; StartPos : longint);
  43.   public
  44.     { Public declarations }
  45.     UserDictionaryOpen : boolean;                 { Record if the custom user dictionary was opened ok }
  46.     constructor Create(AOwner : TComponent); override;  { Standard create method }
  47.     procedure Free;                        { Standard free method }
  48.     procedure SetMaximumSuggestions(Max : byte);      { Method to set the maximum number of suggestions }
  49.     procedure SetDictionaryMain(Filename : string);   { Set a new main dictionary filename }
  50.     procedure SetDictionaryUser(Filename : string);   { Set a new user dictionary filename }
  51.     property DictionaryOpen : boolean read FDictionaryOpen;
  52.  published
  53.     { Published declarations }
  54.     procedure CheckRich(TheEditor : TRichEdit);   { Main method, check the spelling of a RichEdit control types }
  55.     procedure CheckRichSelection(TheEditor : TRichEdit); { Alternate method to check only selected text }
  56.     procedure ClearLists;                                { Method to clear the ignore/replace lists }
  57.     property SuggestType : SuggestionType read FSuggestType write FSuggestType default stCloseMatch;
  58.        { Get/Set the initial suggestion type }
  59.     property DictionaryMain : string read FDictionaryMain write FDictionaryMain;
  60.        { Get/Set the name of the main dictionary file }
  61.     property DictionaryUser : string read FDictionaryUser write FDictionaryUser;
  62.        { Get/Set the name of the user dictionary file }
  63.     property MaxSuggestions : byte read FSuggestMax write SetMaximumSuggestions default 10;
  64.        { Get/Set the maximum number of suggestions }
  65.     property LeaveDictionariesOpen : boolean read FLeaveDictionaryOpen write FLeaveDictionaryOpen default TRUE;
  66.        { Get/Set whether the dictionary should be opened/closed after each call }
  67.     property AvoidHighlight : boolean read FAvoidHighlight write FAvoidHighlight default true;
  68.        { Get/Set whether the highlight should be avoided by the dialog }
  69.   end;
  70.  
  71.  
  72. procedure Register;
  73.  
  74. implementation
  75.  
  76. uses BsASpl32;
  77.  
  78.  
  79. procedure Register;  { Standard component registration procedure }
  80. begin
  81.   RegisterComponents('Samples', [TRichSpell]);
  82. end;
  83.  
  84.  
  85. constructor TRichSpell.Create(AOwner : TComponent);
  86. { Standard create method }
  87. begin
  88.   inherited Create(AOwner);           { Make sure the base component to made }
  89.   FSuggestType := stCloseMatch;       { Set the default values }
  90.   FAvoidHighlight := true;
  91.   FDictionaryMain := 'acrop.dct';
  92.   FDictionaryUser := 'custom.dct';
  93.   FLeaveDictionaryOpen := TRUE;
  94.   FDictionaryOpen  := FALSE;
  95.   UserDictionaryOpen := FALSE;
  96.   FSuggestMax     := 10;
  97.   IgnoreList := TStringList.Create;   { Create the list of ignored words }
  98.   IgnoreList.Clear;                   { And set it to the way it is needed to be }
  99.   IgnoreList.Sorted := TRUE;
  100.   ReplaceList := TStringList.Create;   { Create the list of words to replace }
  101.   ReplaceList.Clear;                   { And set it up }
  102.   ReplaceList.Sorted := FALSE;
  103.   AlternateList := TStringList.Create; { Create the list of words to replace with }
  104.   AlternateList.Clear;                 { And set it up }
  105.   AlternateList.Sorted := FALSE;
  106.   InitDictionaryData(DictDataPtr);        { Create the internal dictionary data }
  107.   SuggestDlg := TSugDialog.Create(Self);  { Create the dialog box }
  108.   SuggestDlg.DictDataPtr := DictDataPtr;  { And let it know the internal data address }
  109. end;
  110.  
  111. procedure TRichSpell.Free;
  112. { Standard free method }
  113. begin
  114.   if FDictionaryOpen then
  115.     BsASpl32.CloseDictionaries(DictDataPtr);
  116.   ReleaseDictionaryData(DictDataPtr);
  117.   IgnoreList.Free;     { Get rid of the ignore list }
  118.   ReplaceList.Free;    { Get rid of the replacement list }
  119.   AlternateList.Free;  { Get rid of the replacement word list }
  120.   SuggestDlg.Free;     { Get rid of the suggestion dialog box }
  121.   inherited Free;      { and then the base component }
  122. end;
  123.  
  124. procedure TRichSpell.SetMaximumSuggestions(Max : byte);
  125. { Set the maximum number of suggestions to return }
  126. begin
  127.   FSuggestMax := Max;   { And store the value }
  128. end;
  129.  
  130. procedure TRichSpell.SetDictionaryMain(Filename : string);
  131. begin
  132.   if FDictionaryOpen or UserDictionaryOpen then
  133.     begin
  134.       BsASpl32.CloseDictionaries(DictDataPtr);  { Close the dictionaries since filename is changing }
  135.       FDictionaryOpen := FALSE;                 { Mark them as not opened }
  136.       UserDictionaryOpen := FALSE;
  137.     end;
  138.   FDictionaryMain := Filename;
  139. end;
  140.  
  141. procedure TRichSpell.SetDictionaryUser(Filename : string);
  142. begin
  143.   if FDictionaryOpen or UserDictionaryOpen then
  144.     begin
  145.       BsASpl32.CloseDictionaries(DictDataPtr);  { Close the dictionaries since filename is changing }
  146.       FDictionaryOpen := FALSE;                 { Mark them as not opened }
  147.       UserDictionaryOpen := FALSE;
  148.     end;
  149.   FDictionaryUser := Filename;
  150. end;
  151.  
  152. procedure TRichSpell.ClearLists;
  153. begin
  154.   IgnoreList.Clear;                    { Clear the ignore list }
  155.   IgnoreList.Sorted := TRUE;
  156.   ReplaceList.Clear;                   { Clear the list of words to replace }
  157.   ReplaceList.Sorted := FALSE;
  158.   AlternateList.Clear;                 { Clear the list of words to do the replacing with }
  159.   AlternateList.Sorted := FALSE;
  160. end;
  161.  
  162.  
  163. procedure TRichSpell.SetDialogPosition(var TheEditor : TRichEdit; StartPos : longint);
  164. { Set the position of the Suggestion Dialog based on the current line.
  165.   If the dialog window and the editor area do not overlap, or there is no
  166.   possibility of the highlight being covered don't move it. }
  167. var EditorScreen : TPoint;
  168.     SelectPoint  : longint;
  169.     SelectTop    : integer;
  170.     SelectBottom : integer;
  171. begin
  172.   EditorScreen := TheEditor.ClientToScreen(TheEditor.ClientRect.TopLeft);
  173.   if ((EditorScreen.X+TheEditor.Width) < SuggestDlg.Left) or
  174.      (EditorScreen.X > (SuggestDlg.Left+SuggestDlg.Width)) or
  175.      ((EditorScreen.Y+TheEditor.Height) < SuggestDlg.Top) or
  176.      (EditorScreen.Y > (SuggestDlg.Top+SuggestDlg.Height)) then
  177.     exit;  { Not in editor area so exit without bothering to move the dialog }
  178.  
  179.   { Figure out where the current line really is on the screen }
  180.  
  181.  exit;
  182.  { ***** NOTE *****
  183.    The following code is not executed due to a bug in the RichEdit that
  184.    generates an exception if the EM_POSFROMCHAR message is sent to it.
  185.    This means that the spelling dialog box will not move out of the way
  186.    if it is covering the highlighted text in the RichEdit component.
  187.  }
  188.  
  189.   SelectPoint := SendMessage(TheEditor.Handle, EM_POSFROMCHAR, $FFFF+(StartPos shr 16), 0);
  190.   SelectTop := (SelectPoint SHR 16) + EditorScreen.Y;  { Get Position of selection on screen }
  191.   SelectBottom := SelectTop + TheEditor.SelAttributes.Height;
  192.   { See if the highlight could actually be covered by the dialog }
  193.   if (SelectBottom  < SuggestDlg.Top) or
  194.      (SelectTop > (SuggestDlg.Top+SuggestDlg.Height)) then
  195.     exit;  { Not near the highlight, exit without moving }
  196.   { It could be covering the highlight, so move to the top or bottom of screen }
  197.   if SelectBottom > (Screen.Height div 2) then
  198.     SuggestDlg.Top := 20
  199.   else
  200.     SuggestDlg.Top := Screen.Height-SuggestDlg.Height-20;
  201. end;
  202.  
  203. procedure TRichSpell.BaseCheckRich(var TheEditor : TRichEdit;
  204.                                        StartLine : longint; StartCol : integer;
  205.                                        EndLine   : longint; EndCol   : integer);
  206. { The main method for this component.  Test the spelling of the text in the passed RichEdit }
  207. var Done       : boolean;        { Loop control }
  208.     OldHide    : boolean;        { Storage for the original state of the HideSelection property }
  209.     Changed    : boolean;        { Was anything in the control changed? }
  210.     EmptyList  : TStringList;    { Empty list in case user dictionary need to be made }
  211.     TheResult  : integer;        { Temporary storage for ShowModal return value }
  212.     Start      : integer;        { Start of the word }
  213.     WordEnd    : integer;        { End of the word }
  214.     CCol       : integer;        { Current column being checked }
  215.     CLine      : longint;        { Current line being checked }
  216.     OffSet     : integer;        { Temporary offset to start of line }
  217.     StartPosition : longint;     { Starting position of the checked text }
  218.   function StripWord(L : string; var SCol : INTEGER; var EndCol : integer) : string;
  219.   BEGIN
  220.     { Scan for the start of a word }
  221.     WHILE (SCol<= Length(L)) AND (NOT (L[SCol] IN SpellCharSet)) DO
  222.       BEGIN
  223.         Inc(SCol);
  224.       END;
  225.  
  226.     EndCol := SCol;   { Assume the end is the same as the start - i.e. one letter word }
  227.     IF SCol > Length(L) THEN           { No non-letter left on line, so no word found }
  228.       BEGIN
  229.         StripWord := '';
  230.         Exit;
  231.       END;
  232.     { Scan for the end of the word }
  233.     WHILE (EndCol <= Length(L)) AND (L[EndCol] IN (SpellCharSet + [''''])) DO
  234.       BEGIN
  235.         Inc(EndCol);
  236.       END;
  237.     StripWord := Copy(L,SCol,EndCol-SCol);     { Return the word we found }
  238.   END;
  239.   function GetNextWord : STRING;
  240.   var LineLength : integer;
  241.   BEGIN
  242.     GetNextWord := '';
  243.     WITH TheEditor DO
  244.       BEGIN
  245.         LineLength := Length(TheEditor.Lines.Strings[CLine]);
  246.         IF CCol > LineLength THEN
  247.           BEGIN
  248.             Inc(CLine);
  249.             CCol := 1;
  250.           END;
  251.         IF CLine > Lines.Count THEN  { Passed the end of the editor get out of here }
  252.           Exit;
  253.         IF (CLine = Lines.Count) AND (CCol >= Length(Lines.Strings[CLine])) THEN    { Ditto }
  254.           Exit;
  255.         GetNextWord := StripWord(Lines.Strings[CLine], CCol, WordEnd);  { Get the text of the word }
  256.         Start := CCol;                      { Save where this word started }
  257.       END;
  258.   END;
  259. begin
  260.   try
  261.   Changed := FALSE;  { Nothing has been changed yet. }
  262.   OldHide := TheEditor.HideSelection;     { Save the old HideSelection property }
  263.   TheEditor.HideSelection := FALSE;        { and make sure selections are shown }
  264.   SuggestDlg.MaxSuggest := FSuggestMax;  { Set the maximum number of suggestions }
  265.  
  266.   if not FDictionaryOpen then  { Check to see if the dictionary is already open }
  267.     begin
  268.       SuggestDlg.Top := (Screen.Height div 2) - (SuggestDlg.Height div 2);  { Position dialog in center of screen }
  269.       SuggestDlg.Left := (Screen.Width div 2) - (SuggestDlg.Width div 2);
  270.       FDictionaryOpen := BsASpl32.OpenDictionary(DictDataPtr, FDictionaryMain);  { Open the dictionaries }
  271.       if not FDictionaryOpen then
  272.         begin
  273.           MessageDlg('Could not open dictionary', mtError, [mbOK], -1);
  274.           exit;
  275.         end;
  276.       UserDictID := BsASpl32.OpenUserDictionary(DictDataPtr, FDictionaryUser);  { And record if they actually opened }
  277.       if UserDictID < 0 then        { Didn't open so try to make one }
  278.         begin
  279.           EmptyList := TStringList.Create;   { Create and clear to make an empty list }
  280.           EmptyList.Clear;
  281.           UserDictID := BsASpl32.BuildUserDictionary(DictDataPtr, FDictionaryUser, EmptyList);  { Build dictionary }
  282.           EmptyList.Free;  { Free the empty list }
  283.         end;
  284.       UserDictionaryOpen := UserDictID > 0;  { Check to see if dictionary was opened/made }
  285.     end;
  286.   with SuggestDlg do  { The suggestion dialog is used a lot so make it easily accessible }
  287.     begin
  288.       CCol  := StartCol;   { Set to beginning of section to spell check }
  289.       CLine := StartLine;
  290.       SuggestDlg.Caption := 'Suggestions: Scanning...';  { Tell the user we're scanning the text }
  291.       TheEditor.SelStart := SendMessage(TheEditor.Handle, EM_LINEINDEX, CLine, 0);
  292.       TheEditor.SelLength := 0;  { Move to start of text to check }
  293.       StartPosition := TheEditor.SelStart;
  294.       if FAvoidHighlight then  { Calculate a window position if avoiding the highlight }
  295.         SetDialogPosition(TheEditor, TheEditor.SelStart);
  296.       SuggestDlg.WordEdit.Text := '';   { Clear the fields in the dialog window }
  297.       SuggestDlg.SuggestList.Clear;
  298.       SuggestDlg.TheResult := 0;
  299.       SuggestDlg.DisableButtons;        { Disable all but the Cancel button }
  300.       Application.ProcessMessages;      { Give Windows time to draw the window }
  301.       Done := FALSE;            { Assume we aren't done }
  302.       repeat
  303.         StartWord := GetNextWord;       { Get the next word in the control }
  304.         Application.ProcessMessages;    { Give Windows time to process mouse events }
  305.         if StartWord <> '' then
  306.           begin
  307.             IF not BsASpl32.GoodWord(DictDataPtr, StartWord) THEN  { Is the word in the dictionaries? }
  308.               if IgnoreList.IndexOf(Uppercase(StartWord)) = -1 then  { No, is it in the ignore list? }
  309.                 begin  { Word not found and not ignored }
  310.                   OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, CLine, 0);
  311.                   TheEditor.SelStart := OffSet + Start-1;
  312.                   TheEditor.SelLength := (WordEnd-Start);
  313.                   Application.ProcessMessages;   { Give Windows a little time to update things}
  314.                   if ReplaceList.IndexOf(StartWord) = -1 then  { In the replacement list? }
  315.                     begin  { No it isn't in the replace list }
  316.                       case FSuggestType of           { Build an inital list of suggestions }
  317.                         stCloseMatch : SuggestList.Items := BsASpl32.SuggestCloseMatch(DictDataPtr, StartWord, FSuggestMax);
  318.                         stPhoneme    : SuggestList.Items := BsASpl32.SuggestPhoneme(DictDataPtr, StartWord, FSuggestMax);
  319.                         stNoSuggest  : SuggestList.Clear;
  320.                       end;
  321.                       SuggestDlg.TheResult := 0;              { Clear the Dialog result }
  322.                       SuggestDlg.Caption := 'Suggestions';    { Remove "Scanning" from caption }
  323.                       if FAvoidHighlight then                 { Check if the highlight has to be avoided }
  324.                         SetDialogPosition(TheEditor, TheEditor.SelStart);
  325.                       if not SuggestDlg.Visible then          { If dialog isn't visible, make it so }
  326.                         SuggestDlg.Show;
  327.                       SuggestDlg.EnableButtons;      { Enable all the dialog controls }
  328.                       WordEdit.Text := StartWord;    { Setup the Suggestion dialog }
  329.                       NotWord.Text := StartWord;     { Setup the Word we are checking }
  330.                       ActiveControl := BtnIgnore;    { Make the Ignore Button active control }
  331.                       Application.ProcessMessages;   { Allow Windows to update things }
  332.  
  333.                       repeat                            { Give Windows all the time until }
  334.                          Application.ProcessMessages;   { one of the buttons are pressed }
  335.                       until SuggestDlg.TheResult <> 0;
  336.                       SuggestDlg.DisableButtons;    { Disable the buttons }
  337.                       TheResult := SuggestDlg.TheResult;  { Find out what the user did }
  338.                    end
  339.                   else
  340.                     begin
  341.                       TheResult := 101;  { Fake Replace Button being pressed }
  342.                       WordEdit.Text := AlternateList.Strings[ReplaceList.IndexOf(StartWord)]; { And get the replacement word }
  343.                     end;
  344.                    case TheResult of   { Do what the user told us }
  345.                     100 : Done := TRUE;                            { Cancel - end the spell checking }
  346.                     101,
  347.                     105 : begin
  348.                             { Replace - replace the word with the correction }
  349.                             TheEditor.SelText := WordEdit.Text;
  350.                             Changed := TRUE;
  351.                             { Reset the end of word counter to reflect possible difference in word lengths }
  352.                             WordEnd := WordEnd + (Length(WordEdit.Text) - Length(StartWord));
  353.                             if CLine = EndLine then  { If this is the last line to test reset the ending column }
  354.                               EndCol := EndCol + (Length(WordEdit.Text) - Length(StartWord));
  355.                             if TheResult = 105 then { Replace all occurences }
  356.                               begin
  357.                                 ReplaceList.Add(StartWord);
  358.                                 AlternateList.Add(WordEdit.Text);
  359.                               end;
  360.                           end;
  361.                          { Add - the questioned word to the user dictionary }
  362.                     102 : BsASpl32.AddWord(DictDataPtr, StartWord, UserDictID);
  363.                     103 : ; { Ignore just this occurence - Dont' do anything }
  364.                          { Ignore All occurences - add the questioned word to the ignore list }
  365.                     104 : IgnoreList.Add(Uppercase(StartWord));
  366.                   end;
  367.                   SuggestDlg.Caption := 'Suggestions: Scanning...';  { Did something Return to scanning... }
  368.                 end;
  369.           end;
  370.         CCol := WordEnd+1;  { Move to one character after the end of the current word }
  371.       until Done or ((CLine >= EndLine) and (CCol >= EndCol)) or (SuggestDlg.TheResult = 100);
  372.      { Canceled or end of the editor is reached }
  373.     end;
  374.   TheEditor.SelStart := StartPosition;   { Move to start of checked text }
  375.   TheEditor.SelLength := 0;
  376.   if SuggestDlg.Visible then   { Get rid of the dialog, if needed }
  377.     SuggestDlg.Hide;
  378.   if not Changed then    { Let the user know something actually happened }
  379.     MessageDlg('No changes made', mtInformation, [mbOK], -1)
  380.   else
  381.     MessageDlg('Checking complete', mtInformation, [mbOK], -1);
  382.   finally
  383.     if SuggestDlg.Visible then   { Get rid of the dialog, if needed }
  384.       SuggestDlg.Hide;
  385.     if not FLeaveDictionaryOpen then  { Check if the dictionaries should be closed }
  386.       begin
  387.         BsASpl32.CloseDictionaries(DictDataPtr);       { Close the dictionaries  }
  388.         FDictionaryOpen := FALSE;          { Mark them as not opened }
  389.         UserDictionaryOpen := FALSE;
  390.       end;
  391.     TheEditor.HideSelection := OldHide; { Restore the HideSelection property of the control }
  392.   end;
  393. end;
  394.  
  395. procedure TRichSpell.CheckRich(TheEditor : TRichEdit);
  396. begin
  397.   { Call the base function to check the entire Editor }
  398.   BaseCheckRich(TheEditor, 0,1, TheEditor.Lines.Count-1, Length(TheEditor.Lines.Strings[TheEditor.Lines.Count-1]));
  399. end;
  400.  
  401. procedure TRichSpell.CheckRichSelection(TheEditor : TRichEdit);
  402. var CheckStart, CheckLength : integer;
  403.     StartLine, StartCol     : longint;
  404.     EndLine, EndCol         : longint;
  405.     OffSet                  : longint;
  406. begin
  407.   with TheEditor do
  408.     begin
  409.       if SelLength = 0 then  { Make sure there is something selected }
  410.         exit;                { If not then there is nothing to check }
  411.      { Make sure we have a whole word at the start of the selection }
  412.       CheckStart  := SelStart;   { Get the start of the selection }
  413.       CheckLength := SelLength;  { And the length }
  414.       SelLength := 1;  { Only look at one character at a time }
  415.       while (CheckStart <> 0) and (TheEditor.SelText[1] in SpellCharSet) do
  416.         begin
  417.           Dec(CheckStart);         { Move back another charater }
  418.           Inc(CheckLength);        { and expand the length to check }
  419.           if SelStart <> 0 then
  420.             SelStart := SelStart - 1;   { then look at the charcter before that }
  421.           SelLength := 1;
  422.         end;
  423.      { Now make sure we have a whole word at the end of the selection }
  424.       SelStart := CheckStart + CheckLength;  { Move to the end of the selected text }
  425.       SelLength := 1;  { Look at only a single charater }
  426.       while (SelStart < GetTextLen) and (SelText[1] in (SpellCharSet + [''''])) do
  427.         begin
  428.           Inc(CheckLength);          { Expand the selection length by one character }
  429.           if SelStart < GetTextLen then  { And move to the next if possible }
  430.             SelStart := SelStart + 1;
  431.           SelLength := 1;
  432.         end;
  433.     end;
  434.   StartLine := SendMessage(TheEditor.Handle, EM_LINEFROMCHAR, CheckStart, 0);
  435.   OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, StartLine, 0);
  436.   StartCol := CheckStart - OffSet+1;
  437.   EndLine := SendMessage(TheEditor.Handle, EM_LINEFROMCHAR, CheckStart+CheckLength, 0);
  438.   OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, EndLine, 0);
  439.   EndCol := (CheckStart+CheckLength) - OffSet+1;
  440.   BaseCheckRich(TheEditor, StartLine, StartCol, EndLine, EndCol);  { Check the selected region }
  441. end;
  442.  
  443.  
  444. end.
  445.